import React, {
  useState,
  useMemo,
  useCallback,
  useEffect,
  lazy,
  Suspense,
} from "react"
import { BrowserRouter as Router, Routes, Route, Link } from "react-router-dom"

// 1. useMemo 示例：昂贵计算的缓存
const UseMemoExample = () => {
  const [count, setCount] = useState(0)
  const [otherState, setOtherState] = useState(0)

  // 模拟昂贵计算
  const expensiveCalculation = (num: number) => {
    console.log("执行昂贵计算...")
    // 模拟耗时操作
    let result = 0
    for (let i = 0; i < 1000000000; i++) {
      result += num
    }
    console.log("执行昂贵计算完成")

    return result
  }

  // 使用 useMemo 缓存计算结果，只有当 count 变化时才重新计算
  // 不使用 useMemo 直接调用函数
  // 这种方式在每次组件渲染时都会重新计算，不管依赖是否变化
  const nonMemoizedValue = expensiveCalculation(count)

  // 使用 useMemo 缓存计算结果的版本（保留作为对比）
  const memoizedValue = useMemo(() => {
    return expensiveCalculation(count)
  }, [count])

  return (
    <div className="example-section">
      <h2>useMemo 示例</h2>
      <div>
        <p>非缓存计算结果: {nonMemoizedValue}</p>
        <p>缓存计算结果: {memoizedValue}</p>
        <p>计数: {count}</p>
        <p>其他状态: {otherState}</p>
        <button onClick={() => setCount(count + 1)}>增加计数</button>
        <button onClick={() => setOtherState(otherState + 1)}>
          更新其他状态（不会触发昂贵计算）
        </button>
      </div>
      <div className="explanation">
        <p>
          当点击"增加计数"按钮时，count 状态更新，useMemo
          依赖项变化，昂贵计算会重新执行。
          当点击"更新其他状态"按钮时，虽然组件重新渲染，但 count
          没有变化，昂贵计算不会重新执行，直接使用缓存的结果。
        </p>
      </div>
    </div>
  )
}

// 2. useCallback 示例：回调函数的缓存
// const ExpensiveChild = React.memo(({ onClick1 }: { onClick1: () => void }) => {
//   console.log("ExpensiveChild 渲染")
//   return <button onClick={onClick1}>子组件按钮</button>
// })

// 子组件使用 React.memo 包裹，避免不必要的渲染
const ChildComponent = React.memo(({ onClick }: { onClick: () => void }) => {
  console.log("ChildComponent Rendered")
  return <button onClick={onClick}>子组件按钮</button>
})

const UseCallbackExample = () => {
  const [count, setCount] = useState(0)

  // 使用 useCallback 缓存函数，依赖项为空数组，表示函数永远不会重新创建
  const handleClick = useCallback(() => {
    console.log("Button Clicked!")
  }, [])

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => setCount(count + 1)}>Increment Parent</button>
      <ChildComponent onClick={handleClick} />
    </div>
  )
}

// const UseCallbackExample = () => {
//   const [count, setCount] = useState(0)
//   const [otherState, setOtherState] = useState(0)

//   // 不使用 useCallback，每次渲染都会创建新的函数实例
//   const handleClickWithoutCallback = () => {
//     console.log("按钮被点击")
//   }

//   // 使用 useCallback 缓存回调函数，依赖项为空数组，函数实例不会变化
//   const handleClickWithCallback = useCallback(() => {
//     console.log("按钮被点击（缓存版本）")
//   }, [])
//   return (
//     <div className="example-section">
//       <h2>useCallback 示例</h2>
//       <div>
//         <p>父组件计数: {count}</p>
//         <p>其他状态: {otherState}</p>
//         <button onClick={() => setCount(count + 1)}>增加计数</button>
//         <button onClick={() => setOtherState(otherState + 1)}>
//           更新其他状态
//         </button>

//         <div style={{ margin: "20px 0" }}>
//           <p>不使用 useCallback（每次渲染都会导致子组件重新渲染）:</p>
//           <ExpensiveChild onClick1={handleClickWithoutCallback} />

//           <p>使用 useCallback（子组件不会重新渲染）:</p>
//           <ExpensiveChild onClick1={handleClickWithCallback} />
//         </div>
//       </div>
//       <div className="explanation">
//         <p>
//           当父组件重新渲染时，没有使用 useCallback 的函数会创建新的实例，导致
//           React.memo 包装的子组件也重新渲染。 而使用 useCallback
//           的函数保持引用不变，子组件不会重新渲染，提高了性能。
//         </p>
//       </div>
//     </div>
//   )
// }

// 3. React.memo 示例：组件渲染结果的缓存
const ExpensiveComponent = ({ value }: { value: number }) => {
  console.log("ExpensiveComponent 渲染，值:", value)

  // 模拟耗时渲染
  let now = performance.now()
  while (performance.now() - now < 50) {
    // 空循环模拟耗时操作
  }

  return <div>昂贵组件渲染结果: {value}</div>
}

// 使用 React.memo 包装组件
const MemoizedExpensiveComponent = React.memo(ExpensiveComponent)

const ReactMemoExample = () => {
  const [count1, setCount1] = useState(0)
  const [count2, setCount2] = useState(0)

  return (
    <div className="example-section">
      <h2>React.memo 示例</h2>
      <div>
        <div>
          <p>未优化组件 (count1: {count1}):</p>
          <ExpensiveComponent value={count1} />
          <button onClick={() => setCount1(count1 + 1)}>增加 count1</button>
        </div>

        <div style={{ marginTop: "20px" }}>
          <p>使用 React.memo 优化的组件 (count2: {count2}):</p>
          <MemoizedExpensiveComponent value={count2} />
          <button onClick={() => setCount2(count2 + 1)}>增加 count2</button>
        </div>

        <div style={{ marginTop: "20px" }}>
          <button onClick={() => setCount1((c) => c)}>
            重新渲染（count1不变）
          </button>
        </div>
      </div>
      <div className="explanation">
        <p>
          点击"重新渲染"按钮时，父组件重新渲染，但 count1 和 count2
          的值没有变化。 未优化的组件会重新渲染，而使用 React.memo
          包装的组件会跳过渲染，直接复用上次的渲染结果。 当 count1 或 count2
          变化时，对应的组件才会重新渲染。
        </p>
      </div>
    </div>
  )
}

// 4. Suspense 示例：异步加载与加载状态
// 模拟数据加载
const fetchData = () => {
  return new Promise<string[]>((resolve) => {
    setTimeout(() => {
      resolve(["数据项1", "数据项2", "数据项3"])
    }, 2000)
  })
}

// 创建一个资源加载器
const createResource = (promise: Promise<any>) => {
  let status = "pending"
  let result: any
  let suspender = promise.then(
    (r) => {
      status = "success"
      result = r
    },
    (e) => {
      status = "error"
      result = e
    }
  )

  return {
    read() {
      if (status === "pending") {
        throw suspender
      } else if (status === "error") {
        throw result
      } else {
        return result
      }
    },
  }
}

// 数据资源
const dataResource = createResource(fetchData())

// 数据展示组件
const DataComponent = () => {
  const data = dataResource.read()
  return (
    <ul>
      {data.map((item, index) => (
        <li key={index}>{item}</li>
      ))}
    </ul>
  )
}

const SuspenseExample = () => {
  return (
    <div className="example-section">
      <h2>Suspense 示例</h2>
      <div>
        <Suspense fallback={<div>加载中...</div>}>
          <DataComponent />
        </Suspense>
      </div>
      <div className="explanation">
        <p>
          Suspense 组件允许在异步加载数据时显示加载状态。 当 DataComponent
          尝试读取尚未加载完成的数据时，它会抛出一个 Promise。 Suspense 捕获这个
          Promise 并显示 fallback 内容，直到 Promise 解决。
          这种模式使异步数据加载的处理更加声明式和简洁。
        </p>
      </div>
    </div>
  )
}

// 5. 路由懒加载示例
// 懒加载的路由组件
// {
//   const LazyHome = lazy(() =>
//     import("./Home").catch(() => ({
//       default: () => <div>Home 组件加载失败</div>,
//     }))
//   )
//   const LazyAbout = lazy(() =>
//     import("./About").catch(() => ({
//       default: () => <div>About 组件加载失败</div>,
//     }))
//   )
//   const LazyContact = lazy(() =>
//     import("./Contact").catch(() => ({
//       default: () => <div>Contact 组件加载失败</div>,
//     }))
//   )

//   // 模拟这些组件，实际应用中应该是单独的文件
//   // 这里只是为了示例能够运行
//   window.Home = () => <div>首页内容</div>
//   window.About = () => <div>关于我们页面内容</div>
//   window.Contact = () => <div>联系我们页面内容</div>

//   const LazyLoadingExample = () => {
//     return (
//       <div className="example-section">
//         <h2>路由懒加载示例</h2>
//         <Router>
//           <div>
//             <nav>
//               <ul style={{ display: "flex", listStyle: "none", gap: "20px" }}>
//                 <li>
//                   <Link to="/">首页</Link>
//                 </li>
//                 <li>
//                   <Link to="/about">关于</Link>
//                 </li>
//                 <li>
//                   <Link to="/contact">联系</Link>
//                 </li>
//               </ul>
//             </nav>

//             <Suspense fallback={<div>页面加载中...</div>}>
//               <Routes>
//                 <Route path="/" element={<LazyHome />} />
//                 <Route path="/about" element={<LazyAbout />} />
//                 <Route path="/contact" element={<LazyContact />} />
//               </Routes>
//             </Suspense>
//           </div>
//         </Router>
//         <div className="explanation">
//           <p>
//             路由懒加载使用 React.lazy 和动态 import
//             实现代码分割，只有当用户导航到特定路由时才加载对应的组件代码。
//             这种方式可以显著减少初始加载的 JavaScript
//             体积，提高应用的首次加载性能。 Suspense
//             组件在懒加载组件加载过程中显示加载状态，提升用户体验。
//           </p>
//         </div>
//       </div>
//     )
//   }
// }

// 主组件
const PerformanceOptimization = () => {
  return (
    <div className="performance-optimization">
      <h1>React 性能优化技术</h1>

      <UseMemoExample />
      <UseCallbackExample />
      <ReactMemoExample />
      <SuspenseExample />
      {/* <LazyLoadingExample /> */}

      <div className="interview-points">
        <h2>面试考点</h2>
        <ul>
          <li>
            <strong>useMemo 与 useCallback 的区别</strong>
            <ul>
              <li>useMemo 缓存计算结果，适用于昂贵的计算操作</li>
              <li>useCallback 缓存函数引用，适用于传递给子组件的回调函数</li>
              <li>两者都依赖于依赖数组来决定何时重新计算/创建</li>
              <li>过度使用可能导致内存占用增加，应当只在性能关键场景使用</li>
            </ul>
          </li>
          <li>
            <strong>React.memo 的适用场景与限制</strong>
            <ul>
              <li>适用于纯展示组件，props 较少变化的场景</li>
              <li>默认只进行 props 的浅比较，可以提供自定义比较函数</li>
              <li>
                当组件依赖 context 或全局状态时，即使 props 不变也可能重新渲染
              </li>
              <li>不应该过度使用，对于简单组件可能引入的开销大于收益</li>
            </ul>
          </li>
          <li>
            <strong>Suspense 与数据获取</strong>
            <ul>
              <li>Suspense 不仅用于代码分割，还可用于数据获取</li>
              <li>实现基于 Suspense 的数据获取需要特定模式，如资源读取模式</li>
              <li>与传统的 useEffect 数据获取相比，代码更加声明式</li>
              <li>React 18 中的 Suspense 支持服务器组件和流式 SSR</li>
            </ul>
          </li>
          <li>
            <strong>代码分割与懒加载的最佳实践</strong>
            <ul>
              <li>基于路由的代码分割是最常见且有效的策略</li>
              <li>可以结合用户交互（如点击、悬停）预加载组件</li>
              <li>避免过度分割，导致大量小文件请求</li>
              <li>使用 webpack 分析工具识别和优化大型依赖</li>
            </ul>
          </li>
          <li>
            <strong>性能优化的测量与验证</strong>
            <ul>
              <li>使用 React DevTools Profiler 识别不必要的渲染</li>
              <li>使用 Chrome Performance 面板分析运行时性能</li>
              <li>使用 Lighthouse 评估整体应用性能</li>
              <li>建立性能基准和自动化性能测试流程</li>
            </ul>
          </li>
          <li>
            <strong>其他关键性能优化技术</strong>
            <ul>
              <li>
                虚拟列表（如 react-window, react-virtualized）处理大数据集
              </li>
              <li>Web Workers 将耗时计算移至后台线程</li>
              <li>使用不可变数据结构（如 Immer）简化状态更新和比较</li>
              <li>避免过度使用内联函数和对象字面量作为 props</li>
              <li>使用 useTransition 和 useDeferredValue 处理非紧急更新</li>
            </ul>
          </li>
        </ul>
      </div>
    </div>
  )
}

export default PerformanceOptimization
